home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / misccmds.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  20KB  |  950 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * misccmds.c: functions that didn't seem to fit elsewhere
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "param.h"
  17.  
  18. static void check_status __ARGS((BUF *));
  19.  
  20. static char_u *(si_tab[]) = {(char_u *)"if", (char_u *)"else", (char_u *)"while", (char_u *)"for", (char_u *)"do"};
  21.  
  22. /*
  23.  * count the size of the indent in the current line
  24.  */
  25.     int
  26. get_indent()
  27. {
  28.     register char_u *ptr;
  29.     register int count = 0;
  30.  
  31.     for (ptr = ml_get(curwin->w_cursor.lnum); *ptr; ++ptr)
  32.     {
  33.         if (*ptr == TAB)    /* count a tab for what it is worth */
  34.             count += (int)curbuf->b_p_ts - (count % (int)curbuf->b_p_ts);
  35.         else if (*ptr == ' ')
  36.             ++count;            /* count a space for one */
  37.         else
  38.             break;
  39.     }
  40.     return (count);
  41. }
  42.  
  43. /*
  44.  * set the indent of the current line
  45.  * leaves the cursor on the first non-blank in the line
  46.  */
  47.     void
  48. set_indent(size, delete)
  49.     register int size;
  50.     int delete;
  51. {
  52.     int                oldstate = State;
  53.     register int    c;
  54.  
  55.     State = INSERT;        /* don't want REPLACE for State */
  56.     curwin->w_cursor.col = 0;
  57.     if (delete)                            /* delete old indent */
  58.     {
  59.         while ((c = gchar_cursor()), iswhite(c))
  60.             (void)delchar(FALSE);
  61.     }
  62.     if (!curbuf->b_p_et)            /* if 'expandtab' is set, don't use TABs */
  63.         while (size >= (int)curbuf->b_p_ts)
  64.         {
  65.             inschar(TAB);
  66.             size -= (int)curbuf->b_p_ts;
  67.         }
  68.     while (size)
  69.     {
  70.         inschar(' ');
  71.         --size;
  72.     }
  73.     State = oldstate;
  74. }
  75.  
  76. /*
  77.  * Opencmd
  78.  *
  79.  * Add a blank line below or above the current line.
  80.  *
  81.  * Return TRUE for success, FALSE for failure
  82.  */
  83.  
  84.     int
  85. Opencmd(dir, redraw, delspaces)
  86.     int         dir;
  87.     int            redraw;
  88.     int            delspaces;
  89. {
  90.     char_u   *ptr, *p_extra;
  91.     FPOS    old_cursor;             /* old cursor position */
  92.     int        newcol = 0;            /* new cursor column */
  93.     int     newindent = 0;        /* auto-indent of the new line */
  94.     int        n;
  95.     int        truncate = FALSE;    /* truncate current line afterwards */
  96.     int        no_si = FALSE;        /* reset did_si afterwards */
  97.     int        retval = FALSE;        /* return value, default is FAIL */
  98.  
  99.     ptr = strsave(ml_get(curwin->w_cursor.lnum));
  100.     if (ptr == NULL)            /* out of memory! */
  101.         return FALSE;
  102.  
  103.     u_clearline();                /* cannot do "U" command when adding lines */
  104.     did_si = FALSE;
  105.     if (curbuf->b_p_ai || curbuf->b_p_si)
  106.     {
  107.         /*
  108.          * count white space on current line
  109.          */
  110.         newindent = get_indent();
  111.         if (newindent == 0)
  112.             newindent = old_indent;        /* for ^^D command in insert mode */
  113.         old_indent = 0;
  114.  
  115.             /*
  116.              * If we just did an auto-indent, then we didn't type anything on the
  117.              * prior line, and it should be truncated.
  118.              */
  119.         if (dir == FORWARD && did_ai)
  120.             truncate = TRUE;
  121.         else if (curbuf->b_p_si && *ptr != NUL)
  122.         {
  123.             char_u    *p;
  124.             char_u    *pp;
  125.             int        i, save;
  126.  
  127.             if (dir == FORWARD)
  128.             {
  129.                 p = ptr + STRLEN(ptr) - 1;
  130.                 while (p > ptr && isspace(*p))    /* find last non-blank in line */
  131.                     --p;
  132.                 if (*p == '{')                    /* line ends in '{': do indent */
  133.                 {
  134.                     did_si = TRUE;
  135.                     no_si = TRUE;
  136.                 }
  137.                 else                            /* look for "if" and the like */
  138.                 {
  139.                     p = ptr;
  140.                     skipspace(&p);
  141.                     for (pp = p; islower(*pp); ++pp)
  142.                         ;
  143.                     if (!isidchar(*pp))            /* careful for vars starting with "if" */
  144.                     {
  145.                         save = *pp;
  146.                         *pp = NUL;
  147.                         for (i = sizeof(si_tab)/sizeof(char_u *); --i >= 0; )
  148.                             if (STRCMP(p, si_tab[i]) == 0)
  149.                             {
  150.                                 did_si = TRUE;
  151.                                 break;
  152.                             }
  153.                         *pp = save;
  154.                     }
  155.                 }
  156.             }
  157.             else
  158.             {
  159.                 p = ptr;
  160.                 skipspace(&p);
  161.                 if (*p == '}')            /* if line starts with '}': do indent */
  162.                     did_si = TRUE;
  163.             }
  164.         }
  165.         did_ai = TRUE;
  166.         if (curbuf->b_p_si)
  167.             can_si = TRUE;
  168.     }
  169.     if (State == INSERT || State == REPLACE)    /* only when dir == FORWARD */
  170.     {
  171.         p_extra = ptr + curwin->w_cursor.col;
  172.         if (curbuf->b_p_ai && delspaces)
  173.             skipspace(&p_extra);
  174.         if (*p_extra != NUL)
  175.             did_ai = FALSE;         /* append some text, don't trucate now */
  176.     }
  177.     else
  178.         p_extra = (char_u *)"";                /* append empty line */
  179.  
  180.     old_cursor = curwin->w_cursor;
  181.     if (dir == BACKWARD)
  182.         --curwin->w_cursor.lnum;
  183.     if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_t)0, FALSE) == FAIL)
  184.         goto theend;
  185.     mark_adjust(curwin->w_cursor.lnum + 1, MAXLNUM, 1L);
  186.     if (newindent || did_si)
  187.     {
  188.         ++curwin->w_cursor.lnum;
  189.         if (did_si)
  190.         {
  191.             if (p_sr)
  192.                 newindent -= newindent % (int)curbuf->b_p_sw;
  193.             newindent += (int)curbuf->b_p_sw;
  194.         }
  195.         set_indent(newindent, FALSE);
  196.         newcol = curwin->w_cursor.col;
  197.         if (no_si)
  198.             did_si = FALSE;
  199.     }
  200.     curwin->w_cursor = old_cursor;
  201.  
  202.     if (dir == FORWARD)
  203.     {
  204.         if (truncate || State == INSERT || State == REPLACE)
  205.         {
  206.             if (truncate)
  207.                 *ptr = NUL;
  208.             else
  209.                 *(ptr + curwin->w_cursor.col) = NUL;    /* truncate current line at cursor */
  210.             ml_replace(curwin->w_cursor.lnum, ptr, FALSE);
  211.             ptr = NULL;
  212.         }
  213.  
  214.         /*
  215.          * Get the cursor to the start of the line, so that 'curwin->w_row' gets
  216.          * set to the right physical line number for the stuff that
  217.          * follows...
  218.          */
  219.         curwin->w_cursor.col = 0;
  220.  
  221.         if (redraw)
  222.         {
  223.             n = RedrawingDisabled;
  224.             RedrawingDisabled = TRUE;
  225.             cursupdate();                /* don't want it to update srceen */
  226.             RedrawingDisabled = n;
  227.  
  228.             /*
  229.              * If we're doing an open on the last logical line, then go ahead and
  230.              * scroll the screen up. Otherwise, just insert a blank line at the
  231.              * right place. We use calls to plines() in case the cursor is
  232.              * resting on a long line.
  233.              */
  234.             n = curwin->w_row + plines(curwin->w_cursor.lnum);
  235.             if (n == curwin->w_height)
  236.                 scrollup(1L);
  237.             else
  238.                 win_ins_lines(curwin, n, 1, TRUE, TRUE);
  239.         }
  240.         ++curwin->w_cursor.lnum;    /* cursor moves down */
  241.     }
  242.     else if (redraw)                 /* insert physical line above current line */
  243.         win_ins_lines(curwin, curwin->w_row, 1, TRUE, TRUE);
  244.  
  245.     curwin->w_cursor.col = newcol;
  246.     if (redraw)
  247.     {
  248.         updateScreen(VALID_TO_CURSCHAR);
  249.         cursupdate();            /* update curwin->w_row */
  250.     }
  251.     CHANGED;
  252.  
  253.     retval = TRUE;                /* success! */
  254. theend:
  255.     free(ptr);
  256.     return retval;
  257. }
  258.  
  259. /*
  260.  * plines(p) - return the number of physical screen lines taken by line 'p'
  261.  */
  262.     int
  263. plines(p)
  264.     linenr_t    p;
  265. {
  266.     return plines_win(curwin, p);
  267. }
  268.     
  269.     int
  270. plines_win(wp, p)
  271.     WIN            *wp;
  272.     linenr_t    p;
  273. {
  274.     register long        col = 0;
  275.     register char_u        *s;
  276.     register int        lines;
  277.  
  278.     if (!wp->w_p_wrap)
  279.         return 1;
  280.  
  281.     s = ml_get_buf(wp->w_buffer, p, FALSE);
  282.     if (*s == NUL)                /* empty line */
  283.         return 1;
  284.  
  285.     while (*s != NUL)
  286.         col += chartabsize(*s++, col);
  287.  
  288.     /*
  289.      * If list mode is on, then the '$' at the end of the line takes up one
  290.      * extra column.
  291.      */
  292.     if (wp->w_p_list)
  293.         col += 1;
  294.  
  295.     /*
  296.      * If 'number' mode is on, add another 8.
  297.      */
  298.     if (wp->w_p_nu)
  299.         col += 8;
  300.  
  301.     lines = (col + (Columns - 1)) / Columns;
  302.     if (lines <= wp->w_height)
  303.         return lines;
  304.     return (int)(wp->w_height);        /* maximum length */
  305. }
  306.  
  307. /*
  308.  * Count the physical lines (rows) for the lines "first" to "last" inclusive.
  309.  */
  310.     int
  311. plines_m(first, last)
  312.     linenr_t        first, last;
  313. {
  314.     return plines_m_win(curwin, first, last);
  315. }
  316.  
  317.     int
  318. plines_m_win(wp, first, last)
  319.     WIN                *wp;
  320.     linenr_t        first, last;
  321. {
  322.     int count = 0;
  323.  
  324.     while (first <= last)
  325.         count += plines_win(wp, first++);
  326.     return (count);
  327. }
  328.  
  329. /*
  330.  * insert or replace a single character at the cursor position
  331.  */
  332.     void
  333. inschar(c)
  334.     int            c;
  335. {
  336.     register char_u  *p;
  337.     int                rir0;        /* reverse replace in column 0 */
  338.     char_u            *new;
  339.     char_u            *old;
  340.     int                oldlen;
  341.     int                extra;
  342.     colnr_t            col = curwin->w_cursor.col;
  343.     linenr_t        lnum = curwin->w_cursor.lnum;
  344.  
  345.     old = ml_get(lnum);
  346.     oldlen = STRLEN(old) + 1;
  347.  
  348.     rir0 = (State == REPLACE && p_ri && col == 0);
  349.     if (rir0 || State != REPLACE || *(old + col) == NUL)
  350.         extra = 1;
  351.     else
  352.         extra = 0;
  353.  
  354.     new = alloc((unsigned)(oldlen + extra));
  355.     if (new == NULL)
  356.         return;
  357.     memmove((char *)new, (char *)old, (size_t)col);
  358.     p = new + col;
  359.     memmove((char *)p + extra, (char *)old + col, (size_t)(oldlen - col));
  360.     if (rir0)                    /* reverse replace in column 0 */
  361.     {
  362.         *(p + 1) = c;            /* replace the char that was in column 0 */
  363.         c = ' ';                /* insert a space */
  364.         extraspace = TRUE;
  365.     }
  366.     *p = c;
  367.     ml_replace(lnum, new, FALSE);
  368.  
  369.     /*
  370.      * If we're in insert mode and showmatch mode is set, then check for
  371.      * right parens and braces. If there isn't a match, then beep. If there
  372.      * is a match AND it's on the screen, then flash to it briefly. If it
  373.      * isn't on the screen, don't do anything.
  374.      */
  375.     if (p_sm && State == INSERT && (c == ')' || c == '}' || c == ']'))
  376.     {
  377.         FPOS           *lpos, csave;
  378.  
  379.         if ((lpos = showmatch(NUL)) == NULL)        /* no match, so beep */
  380.             beep();
  381.         else if (lpos->lnum >= curwin->w_topline)
  382.         {
  383.             updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
  384.             csave = curwin->w_cursor;
  385.             curwin->w_cursor = *lpos;     /* move to matching char */
  386.             cursupdate();
  387.             showruler(0);
  388.             setcursor();
  389.             cursor_on();        /* make sure that the cursor is shown */
  390.             flushbuf();
  391.             vim_delay();        /* brief pause */
  392.             curwin->w_cursor = csave;     /* restore cursor position */
  393.             cursupdate();
  394.         }
  395.     }
  396.     if (!p_ri)                            /* normal insert: cursor right */
  397.         ++curwin->w_cursor.col;
  398.     else if (State == REPLACE && !rir0)    /* reverse replace mode: cursor left */
  399.         --curwin->w_cursor.col;
  400.     CHANGED;
  401. }
  402.  
  403. /*
  404.  * insert a string at the cursor position
  405.  */
  406.     void
  407. insstr(s)
  408.     register char_u  *s;
  409. {
  410.     register char_u        *old, *new;
  411.     register int        newlen = STRLEN(s);
  412.     int                    oldlen;
  413.     colnr_t                col = curwin->w_cursor.col;
  414.     linenr_t            lnum = curwin->w_cursor.lnum;
  415.  
  416.     old = ml_get(lnum);
  417.     oldlen = STRLEN(old);
  418.     new = alloc((unsigned)(oldlen + newlen + 1));
  419.     if (new == NULL)
  420.         return;
  421.     memmove((char *)new, (char *)old, (size_t)col);
  422.     memmove((char *)new + col, (char *)s, (size_t)newlen);
  423.     memmove((char *)new + col + newlen, (char *)old + col, (size_t)(oldlen - col + 1));
  424.     ml_replace(lnum, new, FALSE);
  425.     curwin->w_cursor.col += newlen;
  426.     CHANGED;
  427. }
  428.  
  429. /*
  430.  * delete one character under the cursor
  431.  *
  432.  * return FAIL for failure, OK otherwise
  433.  */
  434.     int
  435. delchar(fixpos)
  436.     int            fixpos;     /* if TRUE fix the cursor position when done */
  437. {
  438.     char_u        *old, *new;
  439.     int            oldlen;
  440.     linenr_t    lnum = curwin->w_cursor.lnum;
  441.     colnr_t        col = curwin->w_cursor.col;
  442.     int            was_alloced;
  443.  
  444.     old = ml_get(lnum);
  445.     oldlen = STRLEN(old);
  446.  
  447.     if (col >= oldlen)    /* can't do anything (happens with replace mode) */
  448.         return FAIL;
  449.  
  450. /*
  451.  * If the old line has been allocated the deleteion can be done in the
  452.  * existing line. Otherwise a new line has to be allocated
  453.  */
  454.     was_alloced = ml_line_alloced();        /* check if old was allocated */
  455.     if (was_alloced)
  456.         new = old;                            /* use same allocated memory */
  457.     else
  458.     {
  459.         new = alloc((unsigned)oldlen);        /* need to allocated a new line */
  460.         if (new == NULL)
  461.             return FAIL;
  462.         memmove((char *)new, (char *)old, (size_t)col);
  463.     }
  464.     memmove((char *)new + col, (char *)old + col + 1, (size_t)(oldlen - col));
  465.     if (!was_alloced)
  466.         ml_replace(lnum, new, FALSE);
  467.  
  468.     /*
  469.      * If we just took off the last character of a non-blank line, we don't
  470.      * want to end up positioned at the newline.
  471.      */
  472.     if (fixpos && curwin->w_cursor.col > 0 && col == oldlen - 1)
  473.         --curwin->w_cursor.col;
  474.  
  475.     CHANGED;
  476.     return OK;
  477. }
  478.  
  479.     void
  480. dellines(nlines, dowindow, undo)
  481.     long             nlines;            /* number of lines to delete */
  482.     int             dowindow;        /* if true, update the window */
  483.     int                undo;            /* if true, prepare for undo */
  484. {
  485.     int             num_plines = 0;
  486.  
  487.     if (nlines <= 0)
  488.         return;
  489.     /*
  490.      * There's no point in keeping the window updated if we're deleting more
  491.      * than a window's worth of lines.
  492.      */
  493.     if (nlines > (curwin->w_height - curwin->w_row) && dowindow)
  494.     {
  495.         dowindow = FALSE;
  496.         /* flaky way to clear rest of window */
  497.         win_del_lines(curwin, curwin->w_row, curwin->w_height, TRUE, TRUE);
  498.     }
  499.     if (undo && !u_savedel(curwin->w_cursor.lnum, nlines))
  500.         return;
  501.  
  502.     mark_adjust(curwin->w_cursor.lnum, curwin->w_cursor.lnum + nlines - 1, MAXLNUM);
  503.     mark_adjust(curwin->w_cursor.lnum + nlines, MAXLNUM, -nlines);
  504.  
  505.     while (nlines-- > 0)
  506.     {
  507.         if (bufempty())         /* nothing to delete */
  508.             break;
  509.  
  510.         /*
  511.          * Set up to delete the correct number of physical lines on the
  512.          * window
  513.          */
  514.         if (dowindow)
  515.             num_plines += plines(curwin->w_cursor.lnum);
  516.  
  517.         ml_delete(curwin->w_cursor.lnum);
  518.  
  519.         CHANGED;
  520.  
  521.         /* If we delete the last line in the file, stop */
  522.         if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
  523.         {
  524.             curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
  525.             break;
  526.         }
  527.     }
  528.     curwin->w_cursor.col = 0;
  529.     /*
  530.      * Delete the correct number of physical lines on the window
  531.      */
  532.     if (dowindow && num_plines > 0)
  533.         win_del_lines(curwin, curwin->w_row, num_plines, TRUE, TRUE);
  534. }
  535.  
  536.     int
  537. gchar(pos)
  538.     FPOS *pos;
  539. {
  540.     return (int)(*(ml_get_pos(pos)));
  541. }
  542.  
  543.     int
  544. gchar_cursor()
  545. {
  546.     return (int)(*(ml_get_cursor()));
  547. }
  548.  
  549. /*
  550.  * Write a character at the current cursor position.
  551.  * It is directly written into the block.
  552.  */
  553.     void
  554. pchar_cursor(c)
  555.     int c;
  556. {
  557.     *(ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE) + curwin->w_cursor.col) = c;
  558. }
  559.  
  560. /*
  561.  * return TRUE if the cursor is before or on the first non-blank in the line
  562.  */
  563.     int
  564. inindent()
  565. {
  566.     register char_u *ptr;
  567.     register int col;
  568.  
  569.     for (col = 0, ptr = ml_get(curwin->w_cursor.lnum); iswhite(*ptr); ++col)
  570.         ++ptr;
  571.     if (col >= curwin->w_cursor.col)
  572.         return TRUE;
  573.     else
  574.         return FALSE;
  575. }
  576.  
  577. /*
  578.  * skipspace: skip over ' ' and '\t'.
  579.  *
  580.  * note: you must give a pointer to a char_u pointer!
  581.  */
  582.     void
  583. skipspace(pp)
  584.     char_u **pp;
  585. {
  586.     register char_u *p;
  587.     
  588.     for (p = *pp; *p == ' ' || *p == '\t'; ++p)    /* skip to next non-white */
  589.         ;
  590.     *pp = p;
  591. }
  592.  
  593. /*
  594.  * skiptospace: skip over text until ' ' or '\t'.
  595.  *
  596.  * note: you must give a pointer to a char_u pointer!
  597.  */
  598.     void
  599. skiptospace(pp)
  600.     char_u **pp;
  601. {
  602.     register char_u *p;
  603.  
  604.     for (p = *pp; *p != ' ' && *p != '\t' && *p != NUL; ++p)
  605.         ;
  606.     *pp = p;
  607. }
  608.  
  609. /*
  610.  * skiptodigit: skip over text until digit found
  611.  *
  612.  * note: you must give a pointer to a char_u pointer!
  613.  */
  614.     void
  615. skiptodigit(pp)
  616.     char_u **pp;
  617. {
  618.     register char_u *p;
  619.  
  620.     for (p = *pp; !isdigit(*p) && *p != NUL; ++p)
  621.         ;
  622.     *pp = p;
  623. }
  624.  
  625. /*
  626.  * getdigits: get a number from a string and skip over it
  627.  *
  628.  * note: you must give a pointer to a char_u pointer!
  629.  */
  630.  
  631.     long
  632. getdigits(pp)
  633.     char_u **pp;
  634. {
  635.     register char_u *p;
  636.     long retval;
  637.     
  638.     p = *pp;
  639.     retval = atol((char *)p);
  640.     while (isdigit(*p))    /* skip to next non-digit */
  641.         ++p;
  642.     *pp = p;
  643.     return retval;
  644. }
  645.  
  646.     char_u *
  647. plural(n)
  648.     long n;
  649. {
  650.     static char_u buf[2] = "s";
  651.  
  652.     if (n == 1)
  653.         return &(buf[1]);
  654.     return &(buf[0]);
  655. }
  656.  
  657. /*
  658.  * set_Changed is called when something in the current buffer is changed
  659.  */
  660.     void
  661. set_Changed()
  662. {
  663.     if (!curbuf->b_changed)
  664.     {
  665.         change_warning();
  666.         curbuf->b_changed = TRUE;
  667.         check_status(curbuf);
  668.     }
  669. }
  670.  
  671. /*
  672.  * unset_Changed is called when the changed flag must be reset for buffer 'buf'
  673.  */
  674.     void
  675. unset_Changed(buf)
  676.     BUF        *buf;
  677. {
  678.     if (buf->b_changed)
  679.     {
  680.         buf->b_changed = 0;
  681.         check_status(buf);
  682.     }
  683. }
  684.  
  685. /*
  686.  * check_status: called when the status bars for the buffer 'buf'
  687.  *                 need to be updated
  688.  */
  689.     static void
  690. check_status(buf)
  691.     BUF        *buf;
  692. {
  693.     WIN        *wp;
  694.     int        i;
  695.  
  696.     i = 0;
  697.     for (wp = firstwin; wp != NULL; wp = wp->w_next)
  698.         if (wp->w_buffer == buf && wp->w_status_height)
  699.         {
  700.             wp->w_redr_status = TRUE;
  701.             ++i;
  702.         }
  703.     if (i && must_redraw < NOT_VALID)        /* redraw later */
  704.         must_redraw = NOT_VALID;
  705. }
  706.  
  707. /*
  708.  * If the file is readonly, give a warning message with the first change.
  709.  * Don't use emsg(), because it flushes the macro buffer.
  710.  * If we have undone all changes b_changed will be FALSE, but b_did_warn
  711.  * will be TRUE.
  712.  */
  713.     void
  714. change_warning()
  715. {
  716.     if (curbuf->b_did_warn == FALSE && curbuf->b_changed == 0 && curbuf->b_p_ro)
  717.     {
  718.         curbuf->b_did_warn = TRUE;
  719.         MSG("Warning: Changing a readonly file");
  720.         sleep(1);            /* give him some time to think about it */
  721.     }
  722. }
  723.  
  724. /*
  725.  * ask for a reply from the user, a 'y' or a 'n'.
  726.  * No other characters are accepted, the message is repeated until a valid
  727.  * reply is entered or CTRL-C is hit.
  728.  *
  729.  * return the 'y' or 'n'
  730.  */
  731.     int
  732. ask_yesno(str)
  733.     char_u *str;
  734. {
  735.     int r = ' ';
  736.  
  737.     while (r != 'y' && r != 'n')
  738.     {
  739.         (void)set_highlight('r');        /* same highlighting as for wait_return */
  740.         msg_highlight = TRUE;
  741.         smsg((char_u *)"%s (y/n)?", str);
  742.         r = vgetc();
  743.         if (r == Ctrl('C'))
  744.             r = 'n';
  745.         msg_outchar(r);        /* show what you typed */
  746.         flushbuf();
  747.     }
  748.     return r;
  749. }
  750.  
  751.     void
  752. msgmore(n)
  753.     long n;
  754. {
  755.     long pn;
  756.  
  757.     if (global_busy)        /* no messages now, wait until global is finished */
  758.         return;
  759.  
  760.     if (n > 0)
  761.         pn = n;
  762.     else
  763.         pn = -n;
  764.  
  765.     if (pn > p_report)
  766.         smsg((char_u *)"%ld %s line%s %s", pn, n > 0 ? "more" : "fewer", plural(pn),
  767.                                             got_int ? "(Interrupted)" : "");
  768. }
  769.  
  770. /*
  771.  * give a warning for an error
  772.  */
  773.     void
  774. beep()
  775. {
  776.     flush_buffers(FALSE);        /* flush internal buffers */
  777.     if (p_vb)
  778.     {
  779.         if (T_VB && *T_VB)
  780.             outstr(T_VB);
  781.         else
  782.         {                        /* very primitive visual bell */
  783.             MSG("    ^G");
  784.             MSG("     ^G");
  785.             MSG("    ^G ");
  786.             MSG("     ^G");
  787.             MSG("       ");
  788.             showmode();            /* may have deleted the mode message */
  789.         }
  790.     }
  791.     else
  792.         outchar('\007');
  793. }
  794.  
  795. /* 
  796.  * Expand environment variable with path name.
  797.  * "~/" is also expanded, like $HOME.
  798.  * If anything fails no expansion is done and dst equals src.
  799.  */
  800.     void
  801. expand_env(src, dst, dstlen)
  802.     char_u    *src;            /* input string e.g. "$HOME/vim.hlp" */
  803.     char_u    *dst;            /* where to put the result */
  804.     int        dstlen;            /* maximum length of the result */
  805. {
  806.     char_u    *tail;
  807.     int        c;
  808.     char_u    *var;
  809.  
  810.     if (*src == '$' || (*src == '~' && STRCHR("/ \t\n", src[1]) != NULL))
  811.     {
  812. /*
  813.  * The variable name is copied into dst temporarily, because it may be
  814.  * a string in read-only memory.
  815.  */
  816.         if (*src == '$')
  817.         {
  818.             tail = src + 1;
  819.             var = dst;
  820.             c = dstlen - 1;
  821.             while (c-- > 0 && *tail && isidchar(*tail))
  822.                 *var++ = *tail++;
  823.             *var = NUL;
  824. /*
  825.  * It is possible that vimgetenv() uses IObuff for the expansion, and that the
  826.  * 'dst' is also IObuff. This works, as long as 'var' is the first to be copied
  827.  * to 'dst'!
  828.  */
  829.             var = vimgetenv(dst);
  830.         }
  831.         else
  832.         {
  833.             var = vimgetenv((char_u *)"HOME");
  834.             tail = src + 1;
  835.         }
  836.         if (var && (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
  837.         {
  838.             STRCPY(dst, var);
  839.             STRCAT(dst, tail);
  840.             return;
  841.         }
  842.     }
  843.     STRNCPY(dst, src, (size_t)dstlen);
  844. }
  845.  
  846. /* 
  847.  * Replace home directory by "~/"
  848.  * If anything fails dst equals src.
  849.  */
  850.     void
  851. home_replace(src, dst, dstlen)
  852.     char_u    *src;            /* input file name */
  853.     char_u    *dst;            /* where to put the result */
  854.     int        dstlen;            /* maximum length of the result */
  855. {
  856.     char_u    *home;
  857.     size_t    len;
  858.  
  859.     /*
  860.      * If there is no "HOME" environment variable, or when it
  861.      * is very short, don't replace anything.
  862.      */
  863.     if ((home = vimgetenv((char_u *)"HOME")) == NULL || (len = STRLEN(home)) <= 1)
  864.         STRNCPY(dst, src, (size_t)dstlen);
  865.     else
  866.     {
  867.         skipspace(&src);
  868.         while (*src && dstlen > 0)
  869.         {
  870.             if (STRNCMP(src, home, len) == 0)
  871.             {
  872.                 src += len;
  873.                 if (--dstlen > 0)
  874.                     *dst++ = '~';
  875.             }
  876.             while (*src && *src != ' ' && --dstlen > 0)
  877.                 *dst++ = *src++;
  878.             while (*src == ' ' && --dstlen > 0)
  879.                 *dst++ = *src++;
  880.         }
  881.         *dst = NUL;
  882.     }
  883. }
  884.  
  885. /*
  886.  * Compare two file names and return TRUE if they are different files.
  887.  * For the first name environment variables are expanded
  888.  */
  889.     int
  890. fullpathcmp(s1, s2)
  891.     char_u *s1, *s2;
  892. {
  893. #ifdef UNIX
  894.     struct stat st1, st2;
  895.     char_u buf1[MAXPATHL];
  896.  
  897.     expand_env(s1, buf1, MAXPATHL);
  898.     if (stat((char *)buf1, &st1) == 0 && stat((char *)s2, &st2) == 0 &&
  899.                 st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
  900.         return FALSE;
  901.     return TRUE;
  902. #else
  903.     char_u buf1[MAXPATHL];
  904.     char_u buf2[MAXPATHL];
  905.  
  906.     expand_env(s1, buf2, MAXPATHL);
  907.     if (FullName(buf2, buf1, MAXPATHL) == OK && FullName(s2, buf2, MAXPATHL) == OK)
  908.         return STRCMP(buf1, buf2);
  909.     /*
  910.      * one of the FullNames() failed, file probably doesn't exist.
  911.      */
  912.     return TRUE;
  913. #endif
  914. }
  915.  
  916. /*
  917.  * get the tail of a path: the file name.
  918.  */
  919.     char_u *
  920. gettail(fname)
  921.     char_u *fname;
  922. {
  923.     register char_u *p1, *p2;
  924.  
  925.     for (p1 = p2 = fname; *p2; ++p2)    /* find last part of path */
  926.     {
  927.         if (ispathsep(*p2))
  928.             p1 = p2 + 1;
  929.     }
  930.     return p1;
  931. }
  932.  
  933. /*
  934.  * return TRUE if 'c' is a path separator.
  935.  */
  936.     int
  937. ispathsep(c)
  938.     int c;
  939. {
  940. #ifdef UNIX
  941.     return (c == PATHSEP);        /* UNIX has ':' inside file names */
  942. #else
  943. # ifdef MSDOS
  944.     return (c == ':' || c == PATHSEP || c == '/');
  945. # else
  946.     return (c == ':' || c == PATHSEP);
  947. # endif
  948. #endif
  949. }
  950.